home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume2 / window / part3 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  51.8 KB

  1. From: Tom Truscott <decvax!mcnc!rti-sel!trt>
  2. Subject: wm - a window manager (part 3 of 4)
  3. Newsgroups: mod.sources
  4. Approved: john@genrad.UUCP
  5.  
  6. Mod.sources:  Volume 2, Issue 33
  7. Submitted by: Tom Truscott <decvax!mcnc!rti-sel!trt>
  8.  
  9.  
  10. #! /bin/sh
  11. # This is a shell archive, meaning:
  12. # 1. Remove everything above the #! /bin/sh line.
  13. # 2. Save the resulting text in a file.
  14. # 3. Execute the file with /bin/sh (not csh) to create the files:
  15. #    misc.c
  16. #    save.c
  17. #    shell.c
  18. #    vterm.c
  19. #    wlist.c
  20. #    wm.c
  21. # This archive created: Fri Aug  2 13:13:19 1985
  22. export PATH; PATH=/bin:$PATH
  23. echo shar: extracting "'misc.c'" '(11326 characters)'
  24. if test -f 'misc.c'
  25. then
  26.     echo shar: will not over-write existing file "'misc.c'"
  27. else
  28. sed 's/^X//' << \SHAR_EOF > 'misc.c'
  29. /*
  30.  *************
  31.  * DISTRIBUTION NOTICE  July 30 1985
  32.  * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
  33.  *        Research Triangle Institute, (919) 541-7005.
  34.  * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
  35.  *        Naval Research Laboratory, (202) 767-3365.
  36.  * No claims or warranties of any sort are made for this distribution.
  37.  * General permission is granted to copy, but not for profit,
  38.  * any of this distribution, provided that this notice
  39.  * is always included in the copies.
  40.  *************
  41.  */
  42. /*
  43.  * Miscellaneous routines for the window manager.
  44.  */
  45. #include "wm.h"
  46.  
  47. /*
  48.  * Get next unused slot in window structure array.
  49.  * Returns slot number, or -1 if no slot available.
  50.  */
  51. int GetSlot()
  52. {
  53.     register int w;
  54.  
  55.     for (w = MINWINDOW; w < MAXWINDOWS; w++)
  56.     if (!(win[w].flags&INUSE))
  57.         return(w);
  58.     
  59.     return(-1);
  60. }
  61.  
  62. /*
  63.  * Prompt user for a window name.
  64.  */
  65. askwindow()
  66. {
  67.     register int w, c;
  68.  
  69.  
  70.     w = -1;
  71.     c = tty_getch();
  72.  
  73.     if (c == CANCEL1  ||  c == CANCEL2)
  74.     showmsg("Canceled.");
  75.  
  76.     else if (c == 'l')
  77.     {
  78.     if (iswindow(lastw))
  79.         w = lastw;
  80.     else
  81.         showmsg("No last window.");
  82.     }
  83.  
  84.     else
  85.     {
  86.     if ( ! isdigit(c))
  87.         showmsg("Indicate window by number, or 'l' for last window.");
  88.     else if ( ! iswindow(ctoi(c)))
  89.         showmsg("Window #%d does not exist.", ctoi(c));
  90.     else
  91.         w = ctoi(c);
  92.     }
  93.  
  94.     return(w);
  95. }
  96.  
  97. /*
  98.  * Reshape window.
  99.  * Returns 0 on normal completion, -1 otherwise.
  100.  * On abnormal completion (e.g. the user cancels)
  101.  * if this is a new window (flag) it will be deleted,
  102.  * otherwise it is restored to its original state..
  103.  * In the impossible(?) event that the window cannot
  104.  * be restored it is deleted, sorry.
  105.  */
  106. getbounds(w, flag)
  107. register int w;
  108. int flag;
  109. {
  110.     register WINDOW *wp, *twp;
  111.  
  112.     /* Unpleasant hack: we save the real window contents while
  113.      * a stunt double gets moved about.
  114.      */
  115.     wp = win[w].wptr;
  116.     if ((win[w].wptr=newwin(wlines(wp),wcols(wp),wbegy(wp),wbegx(wp)))==NULL) {
  117.     win[w].wptr = wp;
  118.     showmsg("Cannot allocate temporary window!");
  119.     return(-1);
  120.     }
  121.  
  122.     showmsg("Move cursor to lower left corner (using hjkl), then type x.");
  123.     if (getpos(w, 0) != 0) {
  124.     delwin(win[w].wptr);
  125.     win[w].wptr = wp;
  126.     if (flag||NewWindow(w, wlines(wp), wcols(wp), wbegy(wp), wbegx(wp))) {
  127.         WListDelete(w);
  128.         FreeWindow(w);
  129.     }
  130.     RedrawScreen();
  131.     return(-1);
  132.     }
  133.  
  134.     showmsg("Now move cursor to upper right corner, then type x.");
  135.     if (getpos(w, 1) != 0) {
  136.     delwin(win[w].wptr);
  137.     win[w].wptr = wp;
  138.     if (flag||NewWindow(w, wlines(wp), wcols(wp), wbegy(wp), wbegx(wp))) {
  139.         WListDelete(w);
  140.         FreeWindow(w);
  141.     }
  142.     RedrawScreen();
  143.     return(-1);
  144.     }
  145.  
  146.     twp = win[w].wptr;
  147.     win[w].wptr = wp;
  148.     if (NewWindow(w, wlines(twp), wcols(twp), wbegy(twp), wbegx(twp))) {
  149.     delwin(twp);
  150.     WListDelete(w);
  151.     FreeWindow(w);
  152.     RedrawScreen();
  153.     return(-1);
  154.     }
  155.     delwin(twp);
  156.     RedrawScreen();
  157.     return(0);
  158. }
  159.  
  160. /*
  161.  * Key definitions used only by routine getpos
  162.  * These keys are used only for entering position of new window
  163.  */
  164. # define RIGHTCHAR    'l'
  165. # define UPCHAR        'k'
  166. # define LEFTCHAR    'h'
  167. # define DOWNCHAR    'j'
  168. # define BIGRIGHTCHAR    'L'    /* jump            */
  169. # define BIGUPCHAR    'K'    /* one-fifth of the    */
  170. # define BIGLEFTCHAR    'H'    /* way across        */
  171. # define BIGDOWNCHAR    'J'    /* the screen        */
  172. # define EXECCHAR    'x'
  173.  
  174. /*
  175.  * move window on screen using UPCHAR, etc.
  176.  * If flag is 0, then window is dragged at lower left.
  177.  * If flag is non-zero, then window is re-sized at upper right.
  178.  * Does not permit bottom (y=LINES-1) line, as it is saved for messages
  179.  * Returns 0 on normal completion, -1 if user cancels.
  180.  */
  181. getpos(w, flag)
  182.  
  183. int w, flag;
  184. {
  185.     register WINDOW *wp;
  186.     register int x0, y0;
  187.     register int c;
  188.     int bigvert, bighoriz;
  189.     int lines, cols;    /* original size of window */
  190.     int aline, acol;    /* 'anchored' corner of window */
  191.     int top, bot, left, right;
  192.  
  193.     bigvert=LINES/5+1;
  194.     bighoriz=COLS/5+1;
  195.  
  196.     wp = win[w].wptr;
  197.     lines = wlines(wp);
  198.     cols = wcols(wp);
  199.     y0 = wbegy(wp)+lines-1;
  200.     x0 = wbegx(wp);
  201.     if (flag) {    /* re-size box */
  202.     aline = y0;
  203.     acol = x0;
  204.     y0 = wbegy(wp);
  205.     x0 = wbegx(wp)+cols-1;
  206.     }
  207.     RedrawScreen();
  208.     (void) movecursor(y0,x0);
  209.     (void) fflush(stdout);
  210.  
  211.     while ((c = tty_getch()) != EXECCHAR)
  212.     {
  213.     switch (c)
  214.     {
  215.     case KEY_HOME:        x0=y0=0;    break;
  216.     case KEY_RIGHT:
  217.     case RIGHTCHAR:        x0 += 1;    break;
  218.     case KEY_UP:
  219.     case UPCHAR:        y0 -= 1;    break;
  220.     case KEY_BACKSPACE:
  221.     case KEY_LEFT:
  222.     case LEFTCHAR:        x0 -= 1;    break;
  223.     case KEY_DOWN:
  224.     case DOWNCHAR:        y0 += 1;    break;
  225.     case BIGRIGHTCHAR:    x0 += bighoriz;    break;
  226.     case BIGUPCHAR:        y0 -= bigvert;    break;
  227.     case BIGLEFTCHAR:    x0 -= bighoriz;    break;
  228.     case BIGDOWNCHAR:    y0 += bigvert;    break;
  229.     default:
  230.         if (c == CANCEL1  ||  c == CANCEL2)
  231.         {
  232.         showmsg("Canceled.");
  233.         return(-1);
  234.         }
  235.         else
  236.         flash();
  237.         break;
  238.     }
  239.     x0 = MAX(x0, 0); x0 = MIN(x0, COLS-1);
  240.     y0 = MAX(y0, 0); y0 = MIN(y0, LINES-2);
  241.  
  242.     if (!flag) {    /* drag box */
  243.         bot = y0;
  244.         left = x0;
  245.         top = y0+1 - lines; top = MAX(top, 0);
  246.         right = x0+cols-1; right = MIN(right, COLS-1);
  247.     } else {    /* re-size box */
  248.         bot = MAX(y0, aline);
  249.         left = MIN(x0, acol);
  250.         top = MIN(y0, aline);
  251.         right = MAX(x0, acol);
  252.     }
  253.     if (NewWindow(w, bot+1-top, right+1-left, top, left))
  254.         return(-1);
  255.     wp = win[w].wptr;
  256.     if (!tty_inputpending()) {
  257.         RedrawScreen();
  258.         (void) movecursor(y0,x0);
  259.         (void) fflush(stdout);
  260.     }
  261.     }
  262.  
  263.     return(0);
  264. }
  265.  
  266. /*
  267.  * If c is a control character, make it printable,
  268.  * e.g. '\007' ==> '^G'.
  269.  */
  270. char *
  271. mkprint(c)
  272.  
  273. register int c;
  274. {
  275.     static char pbuf[3];
  276.  
  277.  
  278.     pbuf[0] = (c>='\040' && c<'\177'   ?   c   :   '^');
  279.     pbuf[1] = (c<'\040' ? c+0100 : c<'\177' ? '\0' : '?');
  280.     pbuf[2] = '\0';
  281.  
  282.     return(pbuf);
  283. }
  284.  
  285. /*
  286.  * Send a setenv command for wmvirt terminal to shell in window w.
  287.  * Note: this is a sad kludge.  If fails if 'vi' or anything
  288.  * other than the wm-activated shell is active in the window.
  289.  * It is rumored that 4.3 BSD supports an ioctl to change
  290.  * the window size (and corresponding signals that are understood
  291.  * by screen managers).  That will provide a better alternative.
  292.  * Note: the setenv hack will still be needed for sessions
  293.  * on remote machines via "tip".
  294.  * Rlogin should (in 4.2 BSD does not) pass along TERMCAP
  295.  * in addition to TERM.
  296.  *
  297.  * mode 0 -- disconnect termcap (unlink sneakytermcap file)
  298.  * mode 1 -- set termcap, attempting sneaky termcap method first.
  299.  * mode 2 -- set termcap, storing termcap string in environment
  300.  * mode 3 -- set termcap by writing a shell command to the window
  301.  */
  302. XSetTerm(w, mode)
  303.  
  304. register int w, mode;
  305. {
  306.     register int i, fd;
  307.     register char *s, *lasts;
  308.  
  309. #ifdef SNEAKYTERMCAP
  310.     if (mode < 3) {
  311. /*
  312.  * Use of /tmp to hold the termcap files is a security hole
  313.  * on most UNIX systems.  Safer, but more trouble,
  314.  * would be to put these files in a directory in the
  315.  * users home directory.
  316.  */
  317.     char termfile[100];
  318.     int oldmask;
  319.     (void) sprintf(termfile, "/tmp/WM.%d.%d",
  320.             (mode==1? getppid(): getpid()), w);
  321.     (void) unlink(termfile);
  322.     if (mode == 0)
  323.         return;
  324.     if (mode == 1) {
  325.         (void) setenv("TERM", "wmvirt");
  326.         (void) setenv("TERMCAP", termfile);
  327.     }
  328.     s = termcap(w);
  329.     oldmask = umask(0);
  330.     fd = creat(termfile, 0644);
  331.     (void) umask(oldmask);
  332.     if (fd >= 0 && write(fd, s, strlen(s)) == strlen(s)
  333.      && write(fd, "\n", 1) == 1
  334.      && close(fd) == 0)
  335.         return;
  336.     if (fd >= 0)
  337.         (void) close(fd);
  338.     if (mode == 1) {
  339.         (void) setenv("TERMCAP", s);
  340.         return;
  341.     }
  342.     /* gotta do it the ugly way ... */
  343.     }
  344. #endif
  345.  
  346.     if (mode == 0)
  347.     return;
  348.  
  349.     /* As suggested by Dave Eckhardt (psuvax1!dae), we check for
  350.      * shellnames *ending* with csh as a clue that a csh is runnning.
  351.      * (This check is also made by the SUSPEND command.)
  352.      */
  353.     if ((i = strlen(shellname)) >= 3
  354.      && strcmp(shellname+i-3,"csh") == 0)
  355.     s = "\nsetenv TERM wmvirt; setenv TERMCAP '";
  356.     else
  357.     s = "\nexport TERM TERMCAP; TERM=wmvirt; TERMCAP='";
  358.  
  359.     fd = win[w].pty;
  360.     (void) write(fd, s, strlen(s));
  361.  
  362.  
  363.     s = termcap(w);
  364.     /* This crazy loop attempts to shield special chars from the tty driver,
  365.      * and to fold the lines to avoid bumping into TTYHOG.
  366.      * A TTYHOG of 255 is much too small, but lots of systems have that. */
  367.     lasts = s;
  368.     for (i = 0; s[i]; i++) {
  369.     if (s[i] == killchar() || s[i] == erasechar()) {
  370.         if (i)
  371.         (void) write(fd, s, i);
  372.         (void) write(fd, "\\", 1);
  373.         s += i;
  374.         i = 0;
  375.     }
  376.         else if (s[i] == ':' && i+(s-lasts) > 180 && i > 0 && s[i-1] != '\\') {
  377.         (void) write(fd, s, i+1);
  378.         (void) write(fd, "\\\r:", 3);
  379.         s += i+1;
  380.         lasts = s;
  381.         i = 0;
  382.     }
  383.     }
  384.     (void) write(fd, s, strlen(s));
  385.  
  386.     (void) write(fd, "'\n", 2);
  387. }
  388.  
  389. /*
  390.  * Find the largest unobscured rectangle on the screen,
  391.  * returning its description as (lines, cols, begline, begcol)
  392.  * via reference parameters.
  393.  * The window being fitted is 'w'.
  394.  * Returns -1 if no unobscured rectangle is found.
  395.  *
  396.  * Note: this algorithm is based on one from Jon Bentley's
  397.  * "Programming Pearls" column in the CACM.  Many readers
  398.  * independently discovered the algorithm, including some
  399.  * who wrote to Bentley and got mentioned in his column (sigh).
  400.  * An interesting question is, is there a faster algorithm?
  401.  * (Faster in the worst case, that is.)
  402.  */
  403. fitwindow(w, lp, cp, blp, bcp)
  404. int w, *lp, *cp, *blp, *bcp;
  405. {
  406.     short *wbase;            /* vaguely like a WINDOW pointer */
  407.     register short *wptop, *wpbot;    /* Ye Olde manual code optimization */
  408.     register int x, ytop, ybot;
  409.     int bestarea, bestsofar, besttohere, bestx;
  410.  
  411.     /* Allocate an appropriately sized array */
  412.     if (LINES > 32000
  413.      || (wbase = alloc(LINES*COLS, short)) == NULL)
  414.     return(-1);
  415.  
  416.     /* Compute cumulative coverage table in LINES*COLS steps */
  417.     /* This is probably the slower loop, due to the subroutine call */
  418.     for (x = 0; x < COLS; x++)
  419.     for (ytop=0,wptop=wbase+x; ytop < LINES-1; ytop++,wptop+=COLS)
  420.         wptop[0] = covers(w, ytop, x) + ((ytop > 0)? wptop[-COLS]: 0);
  421.  
  422.     /* Find largest rectangle in LINES*LINES/2*COLS steps */
  423.     bestarea = 0;
  424.     for (ytop = 0; ytop < LINES-1; ytop++) {
  425.     for (ybot  = ytop; ybot < LINES-1; ybot++) {
  426.         /* Find largest rectangle in this strip */
  427.         bestsofar = besttohere = 0;
  428.         wptop = wbase + (ytop-1)*COLS;
  429.         for (x=0,wpbot=wbase+ybot*COLS; x < COLS; x++,wpbot++,wptop++) {
  430.         if (wpbot[0] - ((ytop > 0)? wptop[0]: 0))
  431.             besttohere = 0;
  432.         else if (++besttohere > bestsofar) {
  433.             bestsofar = besttohere;
  434.             bestx = x+1 - bestsofar;
  435.         }
  436.         }
  437.         if (bestsofar*(ybot+1-ytop) > bestarea) {
  438.         bestarea = bestsofar*(ybot+1-ytop);
  439.         *lp = ybot+1-ytop;
  440.         *cp = bestsofar;
  441.         *blp = ytop;
  442.         *bcp = bestx;
  443.         }
  444.     }
  445.     }
  446.     free((char *)wbase);
  447.  
  448.     if (bestarea <= 0)
  449.     return(-1);
  450.     return(0);
  451. }
  452.  
  453. /*
  454.  * Returns "" if n == 1, otherwise "s".
  455.  * Useful for printing messages such as "1 line" or "2 lines".
  456.  */
  457. char *
  458. plural(n)
  459. int n;
  460. {
  461.     return (n == 1? "": "s");
  462. }
  463.  
  464. /*
  465.  * This routine is equivalent to 'malloc',
  466.  * but returns a 'double *' which makes lint happier.
  467.  * If only malloc were declared this way in the lint library
  468.  * this kludge would be unnecessary.
  469.  */
  470. double *
  471. Malloc(n)
  472. unsigned int n;
  473. {
  474.     extern char *malloc();    /* The tyranny of the lint library */
  475.     return((double *)malloc(n));    /* Ignore lint warning */
  476. }
  477. SHAR_EOF
  478. if test 11326 -ne "`wc -c < 'misc.c'`"
  479. then
  480.     echo shar: error transmitting "'misc.c'" '(should have been 11326 characters)'
  481. fi
  482. fi # end of overwriting check
  483. echo shar: extracting "'save.c'" '(4419 characters)'
  484. if test -f 'save.c'
  485. then
  486.     echo shar: will not over-write existing file "'save.c'"
  487. else
  488. sed 's/^X//' << \SHAR_EOF > 'save.c'
  489. /*
  490.  *************
  491.  * DISTRIBUTION NOTICE  July 30 1985
  492.  * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
  493.  *        Research Triangle Institute, (919) 541-7005.
  494.  * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
  495.  *        Naval Research Laboratory, (202) 767-3365.
  496.  * No claims or warranties of any sort are made for this distribution.
  497.  * General permission is granted to copy, but not for profit,
  498.  * any of this distribution, provided that this notice
  499.  * is always included in the copies.
  500.  *************
  501.  */
  502. /*
  503.  * save.c  M. Lennon  2/07/85
  504.  * Save and restore windows between WM sessions.
  505.  */
  506.  
  507. #include "wm.h"
  508.  
  509.  
  510. /* Save windows from this session in HOME/.wmrc.
  511.  *
  512.  * Produces an ascii file containing info
  513.  * necessary for restoring windows in next WM session.
  514.  */
  515. XSave(file)
  516.  
  517. char *file;    /* name of save file */
  518. {
  519.     register FILE *fp;            /* save file pointer */
  520.     register WINDOW *w;
  521.     register int i;
  522.  
  523.  
  524.     if (*file == '\0')
  525.     return(FALSE);
  526.     if ((fp=fopen(file,"w")) == NULL)
  527.     return(FALSE);
  528.  
  529.     fprintf(fp, "%s\n", mkprint(prefix));
  530.  
  531.     for (i=botw; i>=0; i=win[i].next)
  532.     {
  533.     w = win[i].wptr;
  534.     fprintf(fp, "%d %d %d %d %d\n",
  535.       i,
  536.       (win[i].flags&YFLEX)? 0: wlines(w),
  537.       (win[i].flags&XFLEX)? 0: wcols(w),
  538.       wbegy(w), wbegx(w));
  539.     }
  540.  
  541.     (void)fclose(fp);
  542.     return(TRUE);
  543. }
  544.  
  545. /*
  546.  * Restore windows from previous session
  547.  * as stored in ~/.wmrc.
  548.  * Returns number of windows restored (0 on error).
  549.  */
  550. Restore(file)
  551.  
  552. char *file;    /* name of restore file */
  553. {
  554.     register FILE *fp;            /* restore file pointer */
  555.     int w;                /* window index */
  556.     int lines, cols, begline, begcol;    /* window parameters */
  557.     int count;        /* number of windows restored */
  558.     int lshrink, cshrink; /* amount that windows must be shrunk to fit */
  559.     int rc;        /* temporary to hold return code from fscanf */
  560.     char ccbuf[10], tbuf[100];    /* temporary buffers */
  561.     register char *p;    /* temp pointer into tbuf */
  562.  
  563.  
  564.      /* Open save/restore file.
  565.       */
  566.     if (*file == '\0')
  567.     return(0);
  568.     if ((fp=fopen(file,"r")) == NULL) {
  569.     showmsg("\007Cannot read '%s'.", file);
  570.     sleep(2);
  571.     return(0);
  572.     }
  573.     
  574.      /* Read first line of ~/.wmrc to get the WM prefix character.
  575.       */
  576.     if (fscanf(fp,"%2s%1[\n]", ccbuf, tbuf) != 2) {
  577.     (void)fclose(fp);
  578.     showmsg("\007Bad .wmrc file '%s'.", file);
  579.     sleep(2);
  580.     return(0);
  581.     }
  582.     if (ccbuf[0]=='^' && ccbuf[1])
  583.     prefix = (ccbuf[1]=='?' ? '\177' : ccbuf[1]-0100);
  584.     else prefix = ccbuf[0];
  585.  
  586.     
  587.      /* Restore the windows.
  588.       * Stop trying if input error encountered.
  589.       */
  590.     count = 0;
  591.     for (;;)
  592.     {
  593.      /* Read window parameters.
  594.       * Check for end of file, format error,
  595.       * bad window name, and bad window dimensions.
  596.       */
  597.     rc = fscanf(fp,"%d%d%d%d%d%1[\n]",
  598.         &w,&lines,&cols,&begline,&begcol,tbuf);
  599.     if (rc == EOF)
  600.         break;
  601.     if (rc != 6 || lines < 0 || cols < 0 || begline < 0 || begcol < 0
  602.      || w < MINWINDOW || w >= MAXWINDOWS || (win[w].flags&INUSE)) {
  603.         showmsg("\007Bad window entry, file '%s'.", file);
  604.         sleep(2);
  605.         break;
  606.     }
  607.  
  608.     /*
  609.      * check for "flex" windows
  610.      */
  611.     if (lines == 0 && begline == 0) {
  612.         lines = LINES-1;
  613.         win[w].flags |= YFLEX;
  614.     }
  615.     if (cols == 0 && begcol == 0) {
  616.         cols = COLS;
  617.         win[w].flags |= XFLEX;
  618.     }
  619.  
  620.     /* Check for windows which are beyond this screen */
  621.     /* Bug: if .wmrc is updated these windows are omitted! */
  622.     if (begline >= LINES-1 || begcol >= COLS) {
  623.         showmsg("\007Window #%d is off this screen.", w);
  624.         sleep(2);
  625.         continue;
  626.     }
  627.  
  628.     /* Check for windows which must be shrunk to fit */
  629.     /* Bug(?): if .wmrc is updated the new sizes are saved */
  630.     lshrink = cshrink = 0;
  631.     if (begline+lines > LINES-1) {
  632.         lshrink = begline+lines-(LINES-1);
  633.         lines -= lshrink;
  634.     }
  635.     if (begcol+cols > COLS) {
  636.         cshrink = begcol+cols-COLS;
  637.         cols -= cshrink;
  638.     }
  639.     if (lshrink+cshrink) {
  640.         p = tbuf;
  641.         (void)sprintf(p, "\007Window #%d shrunk by", w); p += strlen(p);
  642.         if (lshrink) {
  643.         (void)sprintf(p, " %d line%s%s", lshrink, plural(lshrink),
  644.             cshrink? " and": "");
  645.         p += strlen(p);
  646.         }
  647.         if (cshrink) {
  648.         (void)sprintf(p, " %d column%s", cshrink, plural(cshrink));
  649.         p += strlen(p);
  650.         }
  651.         (void)sprintf(p, ".");
  652.         showmsg("%s", tbuf);
  653.         sleep(2);
  654.     }
  655.  
  656.      /* Construct new window.
  657.       */
  658.     if (NewWindow(w,lines,cols,begline,begcol))
  659.         continue;    /* cannot happen? */
  660.     WListAdd(w);
  661.     count++;
  662.     }
  663.  
  664.     (void)fclose(fp);
  665.     return(count);
  666. }
  667. SHAR_EOF
  668. if test 4419 -ne "`wc -c < 'save.c'`"
  669. then
  670.     echo shar: error transmitting "'save.c'" '(should have been 4419 characters)'
  671. fi
  672. fi # end of overwriting check
  673. echo shar: extracting "'shell.c'" '(7153 characters)'
  674. if test -f 'shell.c'
  675. then
  676.     echo shar: will not over-write existing file "'shell.c'"
  677. else
  678. sed 's/^X//' << \SHAR_EOF > 'shell.c'
  679. /*
  680.  *************
  681.  * DISTRIBUTION NOTICE  July 30 1985
  682.  * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
  683.  *        Research Triangle Institute, (919) 541-7005.
  684.  * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
  685.  *        Naval Research Laboratory, (202) 767-3365.
  686.  * No claims or warranties of any sort are made for this distribution.
  687.  * General permission is granted to copy, but not for profit,
  688.  * any of this distribution, provided that this notice
  689.  * is always included in the copies.
  690.  *************
  691.  */
  692. /*
  693.  * This file contains routines dealing
  694.  * with the window shells.
  695.  */
  696.  
  697. #include "wm.h"
  698. #include <signal.h>
  699. #include <errno.h>
  700.  
  701. static struct sgttyb sgttybuf;
  702. static struct tchars tcharsbuf;
  703. static struct ltchars ltcharsbuf;
  704. static int ttymode;
  705. static int ttydisc;
  706. static int ttyfd;        /* file descriptor for /dev/tty */
  707.  
  708.  
  709. /*
  710.  * Initialize parameters needed for creating new window shells.
  711.  */
  712. XShellInit()
  713. {
  714.     (void) ioctl(0, (int)TIOCGETD, (char*)&ttydisc);
  715.     (void) ioctl(0, (int)TIOCGETC, (char*)&tcharsbuf);
  716.     (void) ioctl(0, (int)TIOCLGET, (char*)&ttymode);
  717.     (void) ioctl(0, (int)TIOCGLTC, (char*)<charsbuf);
  718.     (void) ioctl(0, (int)TIOCGETP, (char*)&sgttybuf);
  719.  
  720.      /*
  721.       * The psuedo-tty driver should probably not produce
  722.       * internal magic delay characters (cf. sys/tty.c (ttyoutput)).
  723.       * It seems easiest to turn off all delays here.
  724.       * (Even that is not all that easy, due to an XTABS glitch.)
  725.       */
  726.     {
  727.     register int i = ALLDELAY;
  728.     if ((sgttybuf.sg_flags&TBDELAY) == XTABS)
  729.         i &= ~TBDELAY;
  730.     sgttybuf.sg_flags &= ~i;
  731.     }
  732.  
  733.      /* We will use 'ttyfd' later when setting
  734.       * controlling terminals for new shells.
  735.       */
  736.     ttyfd = open("/dev/tty", 0);
  737.  
  738.     strcpy(shellpgm, getenv("SHELL") ? getenv("SHELL") : "sh");
  739.     strcpy(shellname, rindex(shellpgm,'/') ? rindex(shellpgm,'/')+1 : shellpgm);
  740. }
  741.  
  742. /*
  743.  * spawn shell process for window number w
  744.  * Finds first available pty and its matching pts and opens them
  745.  * Returns TRUE if it found you some pty's else FALSE
  746.  */
  747. NewShell(w)
  748.  
  749. register int w;
  750. {
  751.     static char ptlist[] = "0123456789abcdef";
  752.     char ptyname[100], ptsname[100];    /* names of pty master/slave devices */
  753.     int fpty, fpts;            /* descriptors for ""   ""    ""     */
  754.     register int c, i;            /* index */
  755.     int ptydisc;
  756.     extern int errno;
  757.  
  758.  
  759.      /* Look for available pty master/slave pair.
  760.       */
  761.     for (c = 'p';; c++) {
  762.     for (i = 0; ptlist[i]; i++) {
  763.         (void) sprintf(ptyname, "/dev/pty%c%c", c, ptlist[i]);
  764.         if ((fpty = open(ptyname, 2)) < 0) {
  765.         if (errno == ENOENT)
  766.             return(-1);
  767.         continue;
  768.         }
  769.         (void) sprintf(ptsname, "/dev/tty%c%c", c, ptlist[i]);
  770.         if ((fpts = open(ptsname, 2)) < 0) {
  771.         (void) close(fpty);
  772.         continue;
  773.         }
  774.         /* This doesn't close the security hole,
  775.          * but it helps avoid certain problems.
  776.          */
  777.         if (ioctl(fpts, (int)TIOCGETD, (char *)&ptydisc) || ptydisc) {
  778.         (void) close(fpts);
  779.         (void) close(fpty);
  780.         continue;
  781.         }
  782.         /* Okay, this one will do */
  783.         goto gottatty;
  784.     }
  785.     }
  786. gottatty:;
  787.     (void) ioctl(fpty, (int)FIOCLEX, (char *)0);
  788.  
  789.  
  790.  
  791.      /* Fork a new shell.
  792.       */
  793.     switch (win[w].pid=fork())
  794.     {
  795.     default:        /* parent */
  796.     (void) close(fpts);
  797.     win[w].pty=fpty;
  798.     break;
  799.  
  800.     case 0:        /* child */
  801.      /* Set up stdin, stdout, stderr streams. */
  802.     dup2(fpts,0); dup2(fpts,1); dup2(fpts,2);
  803.     if (fpts > 2)
  804.         (void) close(fpts);
  805.      /* Set up slave as new controlling terminal. */
  806.     SetCntrlTerm(ptsname);
  807.      /* Set up process groups. */
  808.     SetProcGrp();
  809.      /* Set pty terminal attributes. */
  810.     InitPseudoTty();
  811.      /* Set env variables TERM & TERMCAP. */
  812. #ifdef SNEAKYTERMCAP
  813.     SetTerm(w, 1);
  814. #else
  815.     (void) setenv("TERM", "wmvirt");
  816.     (void) setenv("TERMCAP", termcap(w));
  817. #endif
  818.      /* Exec the shell. */
  819.     execlp(shellpgm, shellname, (char *)0);
  820.     exit(1);            /* exec failed */
  821.     break;
  822.  
  823.     case -1:        /* fork failed */
  824.     (void) close(fpty);
  825.     (void) close(fpts);
  826.     break;
  827.     }
  828.  
  829.     return(win[w].pid < 0);
  830. }
  831.  
  832. /*
  833.  * Set up terminal attributes for new pseudo-tty.
  834.  * The attributes are those of user's regular terminal.
  835.  * This way, the pseudo-tty will behave just like user's terminal.
  836.  */
  837. InitPseudoTty()
  838. {
  839.      /* Set tty discipline, edit characters,
  840.       * mode, etc.
  841.       */
  842.     (void) ioctl(0, (int)TIOCSETP, (char*)&sgttybuf);
  843.     (void) ioctl(0, (int)TIOCSETD, (char*)&ttydisc);
  844.     (void) ioctl(0, (int)TIOCSETC, (char*)&tcharsbuf);
  845.     (void) ioctl(0, (int)TIOCLSET, (char*)&ttymode);
  846.     (void) ioctl(0, (int)TIOCSLTC, (char*)<charsbuf);
  847. }
  848.  
  849. /*
  850.  * Make 'cterm' the new controlling terminal for
  851.  * this process. Use TIOCNOTTY to turn off
  852.  * current control terminal. Then when we open
  853.  * 'cterm', it automatically becomes the new
  854.  * controlling terminal.
  855.  * Can you say 'kludge'? I knew you could.
  856.  */
  857. XSetCntrlTerm(cterm)
  858.  
  859. char *cterm;
  860. {
  861.      /* We really ought to check the return values
  862.       * of these calls. Oh, well.
  863.       */
  864.     (void) ioctl(ttyfd, (int)TIOCNOTTY, (char*)0);
  865.     (void) close(ttyfd);
  866.     ttyfd = open(cterm, 0);
  867.     (void) close(ttyfd);
  868. }
  869.  
  870. /*
  871.  * Set up a new process group for a process.
  872.  * Process group id will be the pid of the current process.
  873.  * Also set up terminal process group for the benefit of
  874.  * csh job control facilities.
  875.  */
  876. XSetProcGrp()
  877. {
  878.     int pgrp;
  879.  
  880.     pgrp = getpid();
  881.     (void) setpgrp(0, pgrp);
  882.     (void) ioctl(0, (int)TIOCSPGRP, (char*)&pgrp);
  883. }
  884.  
  885. /*
  886.  * Kill shell (process group) in window 'w'.
  887.  */
  888. KillShell(w)
  889.  
  890. register int w;
  891. {
  892.     if (win[w].pid <= 0)
  893.     return;
  894.  
  895.      /* Close pty file.
  896.       */
  897.     (void) close(win[w].pty);
  898.  
  899.      /* Send SIGHUP to all process associated
  900.       * with window w.
  901.       */
  902.     (void) kill(win[w].pid, SIGHUP);
  903.  
  904. #ifdef SNEAKYTERMCAP
  905.     SetTerm(w, 0);
  906. #endif
  907.     win[w].pid = 0;
  908. }
  909.  
  910. setenv(name, val)
  911.  
  912. char *name, *val;
  913. {
  914.     register int n, i;
  915.     register char **ep, *oldval;
  916.     char *namecmp();
  917.     extern char **environ;
  918.  
  919.  
  920.     ep = environ;
  921.  
  922.      /* See if the environment variable is already set.
  923.       */
  924.     for (n=0; ep[n]!=NULL; n++)
  925.     if ((oldval=namecmp(name,ep[n])) != NULL)
  926.         break;
  927.     
  928.      /* If the environment variable is already set and
  929.       * the new value is no longer than the old one,
  930.       * we can just overwrite the old one.
  931.       */
  932.     if (ep[n] != NULL && strlen(oldval) >= strlen(val))
  933.     strcpy(oldval, val);
  934.  
  935.      /* Else we have to reallocate (name=value).
  936.       */
  937.     else
  938.     {
  939.      /* If environment variable not already set,
  940.       * we have to reallocate entire 'environ' array
  941.       * with one additional slot in order to add the new variable.
  942.       * Make sure to terminate array with a NULL entry.
  943.       */
  944.     if (ep[n] == NULL)
  945.     {
  946.         if ((ep=alloc(n+2, char*)) == NULL)
  947.         return(-1);
  948.  
  949.         for (i=0; i<n; i++)
  950.         ep[i] = environ[i];
  951.  
  952.         ep[n+1] = NULL;
  953.  
  954.         environ = ep;
  955.     }
  956.  
  957.      /* Allocate space for new variable, add it to 'environ'.
  958.       */
  959.     if ((ep[n]=alloc(strlen(name)+strlen(val)+2, char)) == NULL)
  960.         return(-1);
  961.     (void) sprintf(ep[n], "%s=%s", name, val);
  962.     }
  963.  
  964.     return(0);
  965. }
  966.  
  967. static char *namecmp(s1, s2)
  968.  
  969. register char *s1, *s2;
  970. {
  971.     for ( ; *s1==*s2; s1++,s2++)
  972.     ;
  973.  
  974.     if (*s1 == '\0' && *s2 == '=')
  975.     return(s2+1);
  976.  
  977.     return(NULL);
  978. }
  979. SHAR_EOF
  980. if test 7153 -ne "`wc -c < 'shell.c'`"
  981. then
  982.     echo shar: error transmitting "'shell.c'" '(should have been 7153 characters)'
  983. fi
  984. fi # end of overwriting check
  985. echo shar: extracting "'vterm.c'" '(11456 characters)'
  986. if test -f 'vterm.c'
  987. then
  988.     echo shar: will not over-write existing file "'vterm.c'"
  989. else
  990. sed 's/^X//' << \SHAR_EOF > 'vterm.c'
  991. /*
  992.  *************
  993.  * DISTRIBUTION NOTICE  July 30 1985
  994.  * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
  995.  *        Research Triangle Institute, (919) 541-7005.
  996.  * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
  997.  *        Naval Research Laboratory, (202) 767-3365.
  998.  * No claims or warranties of any sort are made for this distribution.
  999.  * General permission is granted to copy, but not for profit,
  1000.  * any of this distribution, provided that this notice
  1001.  * is always included in the copies.
  1002.  *************
  1003.  */
  1004. /*
  1005.  * This file contains routines for low-level virtual
  1006.  * terminal emulation.
  1007.  */
  1008.  
  1009. #include "wm.h"
  1010.  
  1011. #define FULLWIDTH(wp)    (wcols(wp)==COLS)
  1012. #define FULLSCREEN(wp)    (FULLWIDTH(wp) && wlines(wp)==LINES-1)
  1013.  
  1014. /* Termcap entry for virtual terminals.
  1015.  */
  1016. static char TERMCAP[]="wm|wmvirt|wm virtual terminal:am:bs:ce=\\EK:ho=\\EH:cd=\\EB:cl=\\ES:nd=\\EC:up=\\EA:cm=\\EY%+ %+ :";
  1017. #define TBUFLEN    (sizeof(TERMCAP)+300)    /* max length of termcap string */
  1018.  
  1019. extern char *tgoto(), *tparm();
  1020.  
  1021. /*
  1022.  * Add 'n' characters of 'p' to window 'w' at its current (y, x) coordinate.
  1023.  */
  1024. WMaddbuf(w, p, n)
  1025. int w;
  1026. register char *p;
  1027. register int n;
  1028. {
  1029.     register WINDOW *wp;
  1030.     register int y, x;
  1031.     register int c;
  1032.  
  1033.     /*
  1034.      * Are we in the midst of an escape sequence?
  1035.      */
  1036.     while (win[w].pend[0] && --n >= 0)
  1037.     WMescape(w, toascii(*p++));
  1038.  
  1039.     wp = win[w].wptr;
  1040.     getyx(wp, y, x);
  1041.  
  1042.  
  1043.     while (--n >= 0) switch (c = toascii(*p++))
  1044.     {
  1045.     case '\t':
  1046.     x = (x+8) & ~07;
  1047.     while (x >= wcols(wp)) {
  1048.         WMaddbuf(w, "\n", 1);
  1049.         x -= wcols(wp);
  1050.     }
  1051.     wmove(wp, y = wcury(wp), x);
  1052.     break;
  1053.     case '\n':
  1054.     if (++y >= wlines(wp)) {
  1055.         --y;
  1056.         wmove(wp, 0, x); WMdeleteln(w);
  1057.     }
  1058.     wmove(wp, y, x);
  1059.     break;
  1060.     case '\r':
  1061.     wmove(wp, y, x = 0);
  1062.     break;
  1063.     case '\b':
  1064.     if (x>0) wmove(wp, y, --x);
  1065.     break;
  1066.     case '\007':
  1067.     beep();
  1068.     break;
  1069.     case '\0':
  1070.     break;
  1071.     case ESC:
  1072.     win[w].pend[0] = ESC;
  1073.     win[w].pend[1] = '\0';
  1074.     while (win[w].pend[0] && --n >= 0)
  1075.         WMescape(w, toascii(*p++));
  1076.     getyx(wp, y, x);
  1077.     break;
  1078.     /* Dummy cases to fool pcc into generating a fast switch table */
  1079.     case 01: case 02: case 03: case 04: case 05: case 06:
  1080.     default:
  1081.     if (isprint(c))
  1082.     {
  1083.         waddch(wp, c);
  1084.         if (++x >= wcols(wp)) {
  1085.         if (++y >= wlines(wp)) {
  1086.             --y;
  1087.             wmove(wp, 0, 0); WMdeleteln(w);
  1088.         }
  1089.         wmove(wp, y, x = 0);
  1090.         }
  1091.     }
  1092.     else
  1093.     {
  1094.         char *s = mkprint(c);
  1095.         WMaddbuf(w, s, strlen(s));
  1096.         getyx(wp, y, x);
  1097.     }
  1098.     break;
  1099.     }
  1100. }
  1101.  
  1102. /*
  1103.  * Construct virtual terminal escape sequence
  1104.  * one character at a time.
  1105.  * When escape sequence is complete, perform
  1106.  * the indicated action in window 'w'.
  1107.  */
  1108. WMescape(w, c)
  1109. int w;
  1110. int c;
  1111. {
  1112.     register WINDOW *wp;
  1113.     register int y, x;
  1114.     register char *pend;
  1115.     int oldx, oldy;
  1116.  
  1117.  
  1118.     pend = win[w].pend;
  1119.     wp = win[w].wptr;
  1120.     getyx(wp, y, x);
  1121.  
  1122.      /* ESC-Y is a multi-character escape sequence
  1123.       * so we need to make sure we have all the
  1124.       * characters before we start processing it.
  1125.       */
  1126.     if (c == 'Y' && pend[1] == '\0') { pend[1]=c; pend[2]='\0'; return; }
  1127.  
  1128.     else if (pend[1] == 'Y')
  1129.     {
  1130.     if (pend[2]=='\0') { pend[2]=c; return; }
  1131.     else               { pend[3]=c; c='Y'; }
  1132.     }
  1133.  
  1134.      /* Process escape sequence.
  1135.       */
  1136.     pend[0] = '\0';    /* escape no longer pending */
  1137.     switch (c)
  1138.     {
  1139.     case 'Y':                /* cursor motion */
  1140.     y = oldy = pend[2]-' '; x = oldx = pend[3]-' ';
  1141.     if (x < 0) x = 0;
  1142.     if (y < 0) y = 0;
  1143.     if (x >= wcols(wp)) x = wcols(wp)-1;
  1144.     if (y >= wlines(wp)) y = wlines(wp)-1;
  1145.     if (y != oldy || x != oldx)
  1146.         showmsg("Bad cursor motion to (%d,%d).", oldy, oldx);
  1147.     wmove(wp, y, x);
  1148.     break;
  1149.     case 'K':                /* clear to end of line */
  1150.     wclrtoeol(wp);
  1151.     break;
  1152.     case 'B':                /* clear to bottom of window */
  1153.     wclrtobot(wp);
  1154.     break;
  1155.     case 'H':                /* home cursor */
  1156.     wmove(wp, 0, 0);
  1157.     break;
  1158.     case 'R':                /* visual bell */
  1159.     flash();
  1160.     break;
  1161.     case 'D':                /* delete line */
  1162.     WMdeleteln(w);
  1163.     break;
  1164.     case 'L':                /* insert line */
  1165.     WMinsertln(w);
  1166.     break;
  1167.     case 'P':                /* insert character */
  1168. #ifdef CURSEASSIST
  1169. #ifndef TERMINFO
  1170.     if (FULLWIDTH(wp) && insert_character && !insert_null_glitch) {
  1171.         (void) movecursor(wbegy(wp)+y, wbegx(wp)+x);
  1172.         putp(insert_character);    winsch(curscr, ' ');
  1173.     }
  1174. #endif
  1175. #endif
  1176.     winsch(wp, ' ');
  1177.     break;
  1178.     case 'Q':                /* delete character */
  1179. #ifdef CURSEASSIST
  1180. #ifndef TERMINFO
  1181.     if (FULLWIDTH(wp) && delete_character) {
  1182.         (void) movecursor(wbegy(wp)+y, wbegx(wp)+x);
  1183.         putp(delete_character);    wdelch(curscr);
  1184.     }
  1185. #endif
  1186. #endif
  1187.     wdelch(wp);
  1188.     break;
  1189.     case 'S':                /* erase window */
  1190.     werase(wp);
  1191. #ifdef    CURSEASSIST
  1192.     if (FULLSCREEN(wp) && !msgbirth)
  1193.         clearok(curscr, TRUE);
  1194. #endif
  1195.     break;
  1196.     case 'C':                /* non-destructive blank */
  1197.     if (++x >= wcols(wp)) {
  1198.         WMaddbuf(w, "\n", 1);
  1199.         x = 0;
  1200.     }
  1201.     wmove(wp, wcury(wp), x);
  1202.     break;
  1203.     case 'A':                /* cursor up */
  1204.     if (--y>=0) wmove(wp, y, x);
  1205.     break;
  1206.     case 'O':                /* enter standout mode */
  1207.     wstandout(wp);
  1208.     break;
  1209.     case 'E':                /* leave standout mode */
  1210.     wstandend(wp);
  1211.     break;
  1212.     default:
  1213.     {
  1214.         char *s;
  1215.         s = mkprint(ESC);
  1216.         WMaddbuf(w, s, strlen(s));
  1217.         s = mkprint(c);
  1218.         WMaddbuf(w, s, strlen(s));
  1219.     }
  1220.     break;
  1221.     }
  1222. }
  1223.  
  1224. /*
  1225.  * Insert a line just above the current line of window wp.
  1226.  * The cursor location in wp is not changed.
  1227.  */
  1228. WMinsertln(w)
  1229. int w;
  1230. {
  1231.     register WINDOW *wp;
  1232.     register int curline, curcol;
  1233.  
  1234.     wp = win[w].wptr;
  1235.     wrefresh(wp);    /* smooths scrolling.  Crucial for untouchwin. */
  1236.     winsertln(wp);
  1237.  
  1238. #ifdef CURSEASSIST
  1239.      /* If this terminal has scrolling regions, use them */
  1240.     if (has_scroll_region && FULLWIDTH(wp)) {
  1241.     /* First, get curscr management out of the way. */
  1242.     curline = cursrow(); curcol = curscol();
  1243.     Cmove(wbegy(wp)+wlines(wp)-1, 0);
  1244.     Cdeleteln();
  1245.     Cmove(wbegy(wp)+wcury(wp), 0);
  1246.     Cinsertln();
  1247.     Cmove(curline, curcol);
  1248.     /* now update the screen itself */
  1249.     (void) movecursor(wbegy(wp)+wcury(wp), wbegx(wp)+wcurx(wp));
  1250.     putp(save_cursor);    /* Save since CS garbles cursor */
  1251.     putp(tgoto(change_scroll_region,
  1252.         wbegy(wp)+wlines(wp)-1, wbegy(wp)+wcury(wp)));
  1253.     putp(restore_cursor);    /* CS garbles cursor */
  1254.     putp(scroll_reverse);
  1255.     putp(tgoto(change_scroll_region, LINES-1, 0));
  1256.     putp(restore_cursor);    /* Once again put it back */
  1257.     Untouchwin(wp);
  1258.     }
  1259.  
  1260.     /* Else if this terminal has scrolling rectangles, use them now. */
  1261. #ifdef SET_WINDOW
  1262.     else if (has_scroll_window) {
  1263.     overwrite(wp, curscr);    /* slow but easy */
  1264.     putp(tparm(set_window,
  1265.         wbegy(wp)+wcury(wp), wbegy(wp)+wlines(wp)-1,
  1266.         wbegx(wp), wbegx(wp)+wcols(wp)-1));
  1267.     putp(scroll_reverse);
  1268.     putp(tparm(set_window, 0, LINES-1, 0, COLS-1));
  1269.     /* get back to where curses thinks we are */
  1270.     putp(tgoto(cursor_address, curscol(), cursrow()));
  1271.     Untouchwin(wp);
  1272.     }
  1273. #endif
  1274.  
  1275.     /* Else if this terminal has ins/del line, now is the time */
  1276.     else if (has_insdel_line && FULLWIDTH(wp)) {
  1277.     /* Open a line above current line in window wp,
  1278.      * then delete wp's bottom line.
  1279.      * Perform identical operations on curscr
  1280.      * as we do on the terminal itself.
  1281.      */
  1282.     (void) movecursor(wbegy(wp)+wcury(wp), 0);
  1283.     putp(insert_line);         Cinsertln();
  1284.     (void) movecursor(wbegy(wp)+wlines(wp), 0);
  1285.     putp(delete_line);         Cdeleteln();
  1286.     RestoreMsg();
  1287.     Untouchwin(wp);
  1288.     }
  1289. #endif
  1290. }
  1291.  
  1292. /*
  1293.  * This routine deletes the current line in window wp.
  1294.  * The cursor location in wp is not changed.
  1295.  */
  1296. WMdeleteln(w)
  1297. int w;
  1298. {
  1299.     register WINDOW *wp;
  1300.     register int curline, curcol;
  1301.  
  1302.     wp = win[w].wptr;
  1303.  
  1304.      /*
  1305.       * See what we can do about windows that scroll slowly
  1306.       */
  1307.      if (!(win[w].flags&FAST)) {
  1308.     static int lines_since_refresh = 0;
  1309.     if ((lines_since_refresh += 7) >= wlines(wp)) {
  1310.         wrefresh(wp);
  1311.         lines_since_refresh = 0;
  1312.     }
  1313.     wdeleteln(wp);
  1314. #ifdef BUGGYTERMINFO
  1315.     touchwin(wp);
  1316. #endif
  1317.     return;
  1318.     }
  1319.  
  1320.     wrefresh(wp);    /* smooths scrolling.  Crucial for untouchwin. */
  1321.     wdeleteln(wp);
  1322. #ifdef BUGGYTERMINFO
  1323.     /* wdeleteln neglects first/bottom info for the last line */
  1324.     touchwin(wp);
  1325. #endif
  1326.  
  1327. #ifdef CURSEASSIST
  1328.      /* If we're deleting top line of a full screen window,
  1329.       * this is the same as scrolling.
  1330.       * (We do not this if we have scrolling region support
  1331.       *  and there is a wm message, but what a bother.)
  1332.       */
  1333.     if (FULLSCREEN(wp) && wcury(wp)==0 && !(has_scroll_region && msgbirth))
  1334.     {
  1335.     ZapMsgLine();    /* so it doesn't scroll up into our window */
  1336.     curline = cursrow(); curcol = curscol();
  1337.     Cmove(0, 0); Cdeleteln();
  1338.     Cmove(curline, curcol);
  1339.  
  1340.     /* Cause screen to scroll.
  1341.      * Since wm almost always 'wants' the cursor on LINES-2,
  1342.      * there is a cheap heuristic thrown in.
  1343.      */
  1344.     (void) movecursor(LINES, curcol);
  1345. #ifndef TERMINFO
  1346.     if (cursor_up) {
  1347.         putp(cursor_up);
  1348.         Cmove(LINES-2, curcol);
  1349.     }
  1350. #endif
  1351.     RestoreMsg();
  1352.     Untouchwin(wp);
  1353.     }
  1354.  
  1355.      /* Else if this terminal has scrolling regions, use them. */
  1356.     else if (has_scroll_region && FULLWIDTH(wp)) {
  1357.     curline = cursrow(); curcol = curscol();
  1358.     Cmove(wbegy(wp)+wcury(wp), 0);
  1359.     Cdeleteln();    /* it is about to be deleted */
  1360.     Cmove(wbegy(wp)+wlines(wp)-1, 0);
  1361.     Cinsertln();    /* it is about to be cleared */
  1362.     Cmove(curline, curcol);
  1363.     (void) movecursor(wbegy(wp)+wlines(wp)-1, wbegx(wp)+wcurx(wp));
  1364.     putp(save_cursor);    /* Save since CS garbles cursor */
  1365.     putp(tgoto(change_scroll_region,
  1366.         wbegy(wp)+wlines(wp)-1, wbegy(wp)+wcury(wp)));
  1367.     putp(restore_cursor);    /* put cursor back */
  1368.     putp(scroll_forward);
  1369.     putp(tgoto(change_scroll_region, LINES-1, 0));
  1370.     putp(restore_cursor);        /* put cursor back */
  1371.     Untouchwin(wp);
  1372.     }
  1373.  
  1374.      /* Else if this terminal has scrolling rectangles, use them. */
  1375. #ifdef SET_WINDOW
  1376.      else if (has_scroll_window) {
  1377.     overwrite(wp, curscr);    /* slow but easy */
  1378.     putp(tparm(set_window,
  1379.         wbegy(wp)+wcury(wp), wbegy(wp)+wlines(wp)-1,
  1380.         wbegx(wp), wbegx(wp)+wcols(wp)-1));
  1381.     putp(tgoto(cursor_address, 0, wlines(wp)-1));
  1382.     putp(scroll_forward);
  1383.     putp(tparm(set_window, 0, LINES-1, 0, COLS-1));
  1384.     putp(tgoto(cursor_address, curscol(), cursrow()));
  1385.     Untouchwin(wp);
  1386.     }
  1387. #endif
  1388.  
  1389.     /* Else if this terminal has insdel line, use that. */
  1390.     else if (has_insdel_line && FULLWIDTH(wp)) {
  1391.     /* Open a line below the last line in window wp,
  1392.      * then delete wp's current line.
  1393.      */
  1394.     (void) movecursor(wbegy(wp)+wlines(wp), 0);
  1395.     putp(insert_line);    Cinsertln();
  1396.     (void) movecursor(wbegy(wp)+wcury(wp), 0);
  1397.     putp(delete_line);    Cdeleteln();
  1398.     RestoreMsg();
  1399.     Untouchwin(wp);
  1400.     }
  1401. #endif
  1402. }
  1403.  
  1404. /*
  1405.  * Construct termcap for wmvirt terminal in window w.
  1406.  */
  1407. char *
  1408. termcap(w)
  1409. int w;
  1410. {
  1411.     register WINDOW *wp;
  1412.     static char tbuf[TBUFLEN];    /* termcap buffer */
  1413.  
  1414.     wp = win[w].wptr;
  1415.     (void)sprintf(tbuf, "%sco#%d:li#%d:", TERMCAP, wcols(wp), wlines(wp));
  1416.  
  1417.      /* If terminal scrolls 'quickly', add insert/delete line to termcap. */
  1418.     if ((win[w].flags&FAST)
  1419.      && (has_insdel_line || has_scroll_region || has_scroll_window))
  1420.     strcat(tbuf, "al=\\EL:dl=\\ED:");
  1421.  
  1422.      /* If terminal has insert/delete character options, add them  here */
  1423.     if (insert_character || (enter_insert_mode && exit_insert_mode))
  1424.     strcat(tbuf, "ic=\\EP:");
  1425.     if (delete_character)
  1426.     strcat(tbuf, "dc=\\EQ:");
  1427.  
  1428.      /* If terminal has standout capabilities, add that too. */
  1429.     if (enter_standout_mode && exit_standout_mode)
  1430.     strcat(tbuf, "so=\\EO:se=\\EE:");
  1431.  
  1432.     /* Include vb if terminal has a visual bell */
  1433.     if (flash_screen)
  1434.     strcat(tbuf, "vb=\\ER:");
  1435.  
  1436.     /* Include keypad capabilities if there is room left */
  1437.     if (strlen(tbuf)+strlen(keycap) < TBUFLEN)
  1438.     strcat(tbuf, keycap);
  1439.  
  1440.     return(tbuf);
  1441. }
  1442. SHAR_EOF
  1443. if test 11456 -ne "`wc -c < 'vterm.c'`"
  1444. then
  1445.     echo shar: error transmitting "'vterm.c'" '(should have been 11456 characters)'
  1446. fi
  1447. fi # end of overwriting check
  1448. echo shar: extracting "'wlist.c'" '(3611 characters)'
  1449. if test -f 'wlist.c'
  1450. then
  1451.     echo shar: will not over-write existing file "'wlist.c'"
  1452. else
  1453. sed 's/^X//' << \SHAR_EOF > 'wlist.c'
  1454. /*
  1455.  *************
  1456.  * DISTRIBUTION NOTICE  July 30 1985
  1457.  * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
  1458.  *        Research Triangle Institute, (919) 541-7005.
  1459.  * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
  1460.  *        Naval Research Laboratory, (202) 767-3365.
  1461.  * No claims or warranties of any sort are made for this distribution.
  1462.  * General permission is granted to copy, but not for profit,
  1463.  * any of this distribution, provided that this notice
  1464.  * is always included in the copies.
  1465.  *************
  1466.  */
  1467. /*
  1468.  * Code for rearranging window display order
  1469.  */
  1470.  
  1471. #include "wm.h"
  1472.  
  1473.  
  1474. /*
  1475.  * Make window 'w' the new top window.
  1476.  * Insert 'w' into window list (keeps track
  1477.  * of redraw order).
  1478.  * 'topw' (pointer to the top window) is set to 'w'.
  1479.  * Recompute obscured window status.
  1480.  */
  1481. WListAdd(w)
  1482.  
  1483. register int w;
  1484. {
  1485.     register int wt;    /* temporary window pointer */
  1486.  
  1487.      /* Add w to empty list.
  1488.       */
  1489.     if (botw < 0)
  1490.     botw=w;
  1491.  
  1492.      /* Add w to top of nonempty list.
  1493.       */
  1494.     else
  1495.     {
  1496.     for (wt=botw; win[wt].next>=0; wt=win[wt].next)
  1497.         ;
  1498.     win[wt].next=w;
  1499.     }
  1500.  
  1501.  
  1502.     win[w].next = -1;
  1503.     topw = w;
  1504.  
  1505.     /* Recompute obscured window status */
  1506.     WObscure();
  1507. }
  1508.  
  1509. /*
  1510.  * Delete window 'w'.
  1511.  * Remove it from window list.
  1512.  * Recompute obscured windows.
  1513.  */
  1514. WListDelete(w)
  1515.  
  1516. register int w;
  1517. {
  1518.     register int wt;    /* temporary window pointer */
  1519.  
  1520.     if ( ! iswindow(w))
  1521.     return;
  1522.  
  1523.      /* Don't read from this window any more.
  1524.       */
  1525.     DisablePty(w);
  1526.  
  1527.      /* Delete w from list.
  1528.       */
  1529.     if (botw==w)
  1530.     botw=win[botw].next;
  1531.     else
  1532.     {
  1533.     for (wt=botw; wt>=0; wt=win[wt].next)
  1534.         if (win[wt].next==w) break;
  1535.     if (wt>=0)
  1536.         win[wt].next=win[win[wt].next].next;
  1537.     }
  1538.  
  1539.      /* Find new topw.
  1540.       */
  1541.     wt=botw;
  1542.     while (wt>=0 && win[wt].next>=0)
  1543.     wt=win[wt].next;
  1544.     topw=wt;
  1545.  
  1546.     /* Recompute obscured window info */
  1547.     WObscure();
  1548. }
  1549.  
  1550. /*
  1551.  * Recompute obscured ('blocked') and 'covers' information
  1552.  * Enable/Disable ptys appropriately.
  1553.  */
  1554. WObscure()
  1555. {
  1556.     register int w, wt, i;
  1557.  
  1558.     for (w = botw; w >= 0; w = win[w].next) {
  1559.     EnablePty(w);
  1560.     win[w].flags &= ~BLOCKED;
  1561.     for (i = 0; i < MAXWINDOWS; i++)
  1562.         win[w].covers[i] = FALSE;
  1563.     for (wt = botw; wt != w; wt = win[wt].next) {
  1564.         if (!overlap(w, wt))
  1565.         continue;
  1566.         win[w].covers[wt] = TRUE;
  1567.         win[wt].flags |= BLOCKED;
  1568.         /* We could disable the obscured window's pty at this point,
  1569.          * but we'll wait and do it in 'readptys()'.
  1570.          */
  1571.         /* (we should probably disable it now if it was previously) */
  1572.     }
  1573.     }
  1574. }
  1575.  
  1576. /* Note: the arithmetic 'bottom' is actual the visual 'top'!
  1577.  * Also, RIGHT and TOP are the index of the column (row) just past
  1578.  * the index of the window's actual right (top).
  1579.  */
  1580. #define BOTTOM(wp)    (wbegy(wp))
  1581. #define    TOP(wp)        (BOTTOM(wp)+wlines(wp))
  1582. #define LEFT(wp)    (wbegx(wp))
  1583. #define    RIGHT(wp)    (LEFT(wp)+wcols(wp))
  1584.  
  1585. /*
  1586.  * Determine if two windows overlap.
  1587.  * Windows must have at least a row (column) separating them
  1588.  * to permit the border lines to be drawn.
  1589.  */
  1590. overlap(w1, w2)
  1591.  
  1592. int w1, w2;
  1593. {
  1594.     register WINDOW *wp1, *wp2;
  1595.  
  1596.     wp1 = win[w1].wptr;
  1597.     wp2 = win[w2].wptr;
  1598.     return(LEFT(wp1)   <= RIGHT(wp2)
  1599.     && LEFT(wp2)   <= RIGHT(wp1)
  1600.     && BOTTOM(wp1) <= TOP(wp2)
  1601.     && BOTTOM(wp2) <= TOP(wp1));
  1602. }
  1603.  
  1604. /*
  1605.  * Returns 1 if a window (or its border) above w cover point (y,x),
  1606.  * otherwise returns 0.
  1607.  */
  1608. covers(w, y, x)
  1609. int w;
  1610. register int y, x;
  1611. {
  1612.     register int wt;
  1613.     register WINDOW *wp;
  1614.  
  1615.     for (wt = w; (wt = win[wt].next) >= 0;) {
  1616.     wp = win[wt].wptr;
  1617.     if (LEFT(wp)    <= x+1
  1618.      && x        <= RIGHT(wp)
  1619.      && BOTTOM(wp)    <= y+1
  1620.      && y        <= TOP(wp))
  1621.         return(1);
  1622.     }
  1623.     return(0);
  1624. }
  1625. SHAR_EOF
  1626. if test 3611 -ne "`wc -c < 'wlist.c'`"
  1627. then
  1628.     echo shar: error transmitting "'wlist.c'" '(should have been 3611 characters)'
  1629. fi
  1630. fi # end of overwriting check
  1631. echo shar: extracting "'wm.c'" '(12415 characters)'
  1632. if test -f 'wm.c'
  1633. then
  1634.     echo shar: will not over-write existing file "'wm.c'"
  1635. else
  1636. sed 's/^X//' << \SHAR_EOF > 'wm.c'
  1637. /*
  1638.  *************
  1639.  * DISTRIBUTION NOTICE  July 30 1985
  1640.  * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
  1641.  *        Research Triangle Institute, (919) 541-7005.
  1642.  * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
  1643.  *        Naval Research Laboratory, (202) 767-3365.
  1644.  * No claims or warranties of any sort are made for this distribution.
  1645.  * General permission is granted to copy, but not for profit,
  1646.  * any of this distribution, provided that this notice
  1647.  * is always included in the copies.
  1648.  *************
  1649.  */
  1650. /*
  1651.  * wm.c  R. Jacob  7/28/1980
  1652.  * Simple multiple-window monitor for Unix
  1653.  * allows windows to overlap
  1654.  *
  1655.  * This is the code for the main program
  1656.  *
  1657.  * This version runs as only one process (plus the shells)
  1658.  * This is intended for Berkeley 4.2 VAX Unix only.
  1659.  */
  1660.  
  1661. #include "wm.h"
  1662. #include <signal.h>
  1663. #include <sys/wait.h>
  1664. #include <sys/time.h>
  1665. #include <sys/resource.h>
  1666.  
  1667. #define LINEBUF        64    /* size of pty input buffer */
  1668.  
  1669.  
  1670. /*
  1671.  * Real declarations for stuff defined as extern in wm.h
  1672.  */
  1673. struct win_struct win[MAXWINDOWS];    /* array of windows */
  1674. int botw, topw, lastw;            /* bottom, top, last windows */
  1675. int prefix = '\033';            /* prefix character */
  1676. char savefile[100];            /* name of save/restore file */
  1677. char shellname[20];            /* name of shell */
  1678. char shellpgm[100];            /* pathname of shell */
  1679. int configflag = FALSE;            /* true if .wmrc config. has changed */
  1680. #ifndef TERMINFO
  1681. char *change_scroll_region, *save_cursor, *restore_cursor;
  1682. char *set_window;
  1683. #endif
  1684. int has_scroll_window = FALSE;    /* true if terminal has 'usable' set_window */
  1685. int has_scroll_region = FALSE;    /* true if terminal has 'usable' SR */
  1686. int has_insdel_line = FALSE;    /* true if terminal has both ins+del line */
  1687.  
  1688. static int savereadmask;        /* pty readmask */
  1689. static int childdied = FALSE;        /* set when a window shell stops */
  1690. static int restorflag=TRUE;        /* TRUE if we're restoring windows */
  1691. static int MaxQueued    = 150;
  1692. static long pausetime = 200000L;
  1693. static int clamp_down;    /* number of chars to remain snappy after ctrl-S */
  1694. static int Divisor;    /* read/write reduction factor */
  1695.  
  1696. main(argc, argv)
  1697.  
  1698. int argc;
  1699. char *argv[];
  1700. {
  1701.     register int c;        /* input character */
  1702.     int readmask;        /* temp pty readmask */
  1703.     register int w;        /* window index */
  1704.     register int quit = FALSE;    /* Did user give the 'quit' command? */
  1705.     register struct timeval *tp;
  1706.     struct timeval timeout;
  1707.     register char *s;
  1708.  
  1709.  
  1710.     setbuf(stdout, alloc(BUFSIZ, char));
  1711.     DoCmdArgs(argc, argv);
  1712.  
  1713.     Startup();
  1714.  
  1715.     /* Adjust MaxQueued and pausetime values for fast terminals */
  1716.     {
  1717.     int ttyspeed;
  1718.     if ((ttyspeed = baudrate()) > 4800) {
  1719.         pausetime /= 2;
  1720.         if (ttyspeed > B9600)
  1721.         MaxQueued *= 2;
  1722.     }
  1723.     }
  1724.  
  1725.      /* Try to restore window arrangement from a previous
  1726.       * WM session. 'Restore()' returns number of windows restored.
  1727.       * If no windows restored, start from scratch,
  1728.       * providing user with a full screen window.
  1729.       */
  1730.     ClearScreen();
  1731.     if (restorflag==FALSE || Restore(savefile) <= 0)
  1732.     {
  1733.     if (savefile[0] == '\0')
  1734.         strcpy(savefile, ".wmrc");
  1735.     w = GetSlot();
  1736.     if (NewWindow(w, LINES-1, COLS, 0, 0))
  1737.     {
  1738.         showmsg("Sorry, can't create any windows.");
  1739.         FreeWindow(w);
  1740.         Shutdown(1);
  1741.     }
  1742.     WListAdd(w);
  1743.     }
  1744.     RedrawScreen();
  1745.  
  1746.     showmsg("Welcome to WM. Type %sh for help.", mkprint(prefix));
  1747.     RestoreCursor();
  1748.  
  1749.  
  1750.      /* Main processing loop.
  1751.       */
  1752.     do
  1753.     {
  1754.     if (childdied)
  1755.         ReapShell();
  1756.  
  1757.      /* If the shell in the top window has died (pid == -1),
  1758.       * or was never started to begin with (pid == 0),
  1759.       * start a new shell.
  1760.       */
  1761.     if (win[topw].pid <= 0)
  1762.     {
  1763.         if (win[topw].pid < 0)
  1764.         showmsg("\007Shell in current window died. Restarting...");
  1765.  
  1766.         if (NewShell(topw))
  1767.         {
  1768.         showmsg("\007Sorry, can't start up new shell.");
  1769.         win[topw].pid = -1;
  1770.         }
  1771.         else
  1772.         EnablePty(topw);
  1773.         RestoreCursor();
  1774.     }
  1775.  
  1776.      /* Poll user's terminal and ptys.
  1777.       */
  1778.     readmask = savereadmask;
  1779.     tp = 0;
  1780.     Divisor = (clamp_down? 4: 1);
  1781.  
  1782. #ifdef    TIOCOUTQ
  1783.     {
  1784.         long n;
  1785.         if (ioctl(1, (int)TIOCOUTQ, (char*)&n)==0 && n > MaxQueued/Divisor)
  1786.         {
  1787.         readmask &= 01;
  1788.         tp = &timeout;
  1789.         tp->tv_sec = 0;
  1790.         tp->tv_usec = pausetime/Divisor;
  1791.         }
  1792.     }
  1793. #endif
  1794.  
  1795.     if (select(8*sizeof(readmask), &readmask, 0, 0, tp) <= 0)
  1796.         continue;
  1797.  
  1798.     /* Terminate messages after a few seconds */
  1799.     if (msgbirth && abs(time((time_t *)0) - msgbirth) > 3)
  1800.         showmsg("");
  1801.  
  1802.      /* If no input from the user, read ptys.
  1803.       */
  1804.     if ((readmask&01) == 0) {
  1805.         readptys(readmask);
  1806.         continue;
  1807.     }
  1808.  
  1809.      /* If user input is not the WM command prefix character,
  1810.       * just send input to the appropriate pty.
  1811.       */
  1812.     do {
  1813.         if ((c = tty_getch()) != prefix) {
  1814.         (void)write(win[topw].pty, tty_text, tty_textlen);
  1815.         if (c == CTRL(S))
  1816.             clamp_down = LINES*COLS/2;
  1817.         }
  1818.  
  1819.          /* Process WM command.
  1820.           */
  1821.         else
  1822.         {
  1823.         showmsg("#%d Command?", topw);
  1824.         c = tty_getch();
  1825.         showmsg("");
  1826.         if (c != prefix)
  1827.             quit = docmd(c);
  1828.         else
  1829.             (void)write(win[topw].pty, tty_text, tty_textlen);
  1830.         RestoreCursor();
  1831.         }
  1832.     } while (tty_backcnt > 0);
  1833.     } while ( ! quit);
  1834.  
  1835.  
  1836.      /* If user has changed the window configuration since
  1837.       * the session began, see if they want to save the
  1838.       * current configuration.
  1839.       */
  1840.     if (restorflag && configflag)
  1841.     {
  1842.     showmsg("Save current (modified) window configuration? [no] ");
  1843.     c = tty_getch();
  1844.     if (c != 'y' && c != 'Y')
  1845.         showmsg("");
  1846.     else if ( (s = WPrompt("save file", savefile)) == NULL)
  1847.         ;
  1848.     else if (Save(s) != 0)
  1849.         showmsg("Saved current window configuration in '%s'.", s);
  1850.     else
  1851.         showmsg("Sorry, can't save current window configuration.");
  1852.     }
  1853.  
  1854.  
  1855.      /* Shut down.
  1856.       */
  1857.     Shutdown(0);
  1858. }
  1859.  
  1860. static char USAGE[] = "[ -n ]  [ -f savefile ]";
  1861.  
  1862. /*
  1863.  * Interpret command line arguments to wm.
  1864.  */
  1865. DoCmdArgs(argc, argv)
  1866.  
  1867. register int argc;    /* arg count */
  1868. register char *argv[];    /* arg list */
  1869. {
  1870.     for (argv++,argc--; argc>0; argv++,argc--)
  1871.     {
  1872.     if (**argv != '-')
  1873.     {
  1874.         fprintf(stderr, "usage: wm %s\n", USAGE);
  1875.         exit(1);
  1876.     }
  1877.     switch ((*argv)[1])
  1878.     {
  1879.     case 'f':    /* next arg is name of save/restore file */
  1880.         strcpy(savefile, *++argv);
  1881.         argc--;
  1882.         break;
  1883.     case 'n':    /* don't restore/save window configuration */
  1884.         restorflag = FALSE;
  1885.         break;
  1886.     default:
  1887.         fprintf(stderr, "wm: unknown option '%s'\n", *argv);
  1888.         fprintf(stderr, "usage: wm %s\n", USAGE);
  1889.         exit(1);
  1890.     }
  1891.     }
  1892. }
  1893.  
  1894. /*
  1895.  * Initialize WM.
  1896.  */
  1897. XStartup()
  1898. {
  1899.     register int w;        /* window pointer */
  1900.     int onintr(), sigchild();    /* interrupt handler */
  1901.  
  1902.     savereadmask = 01;
  1903.     ShellInit();    /* this call must precede the suspend()! */
  1904.  
  1905.      /* Catch signals.
  1906.       * Note: there is a tiny window from here to the return of raw().
  1907.       * Signals could be ignored until then, but it is a bother.
  1908.       */
  1909.     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
  1910.     (void)signal(SIGHUP,  onintr);
  1911.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  1912.     (void)signal(SIGQUIT, onintr);
  1913.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  1914.     (void)signal(SIGINT,  onintr);
  1915.     if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
  1916.     (void)signal(SIGPIPE, onintr);
  1917.     (void)signal(SIGCHLD, sigchild);
  1918.  
  1919.      /* Initialize curses stuff.
  1920.       */
  1921.     if ((w = (int)initscr()) == ERR || !cursor_address) {
  1922.     /* This ERR nonsense is for the benefit of terminfo curses.
  1923.      * Has initscr cleaned up correctly (e.g. reset the tty)?
  1924.      * I sure wish initscr always suceeded.
  1925.      */
  1926.     if (w != ERR)
  1927.         endwin();
  1928.     fprintf(stderr, "Sorry.  Need cursor addressing to play WM\n");
  1929.     /* If 'wm' is run via exec from a .profile, then exiting here
  1930.      * would log the luser out.  Unfortunately, we cannot reliably
  1931.      * determine if wm's parent is a shell, so we cannot
  1932.      * simply exit now.
  1933.      */
  1934.     fprintf(stderr, "Spawning a normal shell\n");
  1935.     execlp(shellpgm, shellname, (char *)0);
  1936.     exit(1);
  1937.     }
  1938.     noecho(); raw();
  1939.     leaveok(stdscr, TRUE);
  1940.  
  1941. #ifdef TERMINFO
  1942. #ifdef    BUGGYTERMINFO
  1943.     /* buggyterminfo neglects the move_standout_mode problem */
  1944.     if (!move_standout_mode)
  1945.     set_attributes = enter_standout_mode = exit_standout_mode = NULL;
  1946.     /* in buggyterminfo, idlok leads to core dumps */
  1947. #else
  1948.     idlok(curscr, TRUE);    /* the first arg is pointless, yes? */
  1949. #endif
  1950. #else
  1951.     /*
  1952.      * hack to check for scrolling-region capability (vt100)
  1953.      * since curses does not itself check.
  1954.      */
  1955.     change_scroll_region = getcap("cs");
  1956.     save_cursor = getcap("sc");
  1957.     restore_cursor = getcap("rc");
  1958.     set_window = getcap("sw");
  1959. #ifdef GAGMEKEYPAD
  1960.     init_keypad();
  1961. #endif
  1962. #endif
  1963.  
  1964.     /* ensure there is a 'scroll_forward' string */
  1965.     if (!scroll_forward)
  1966.     scroll_forward = "\n";
  1967.     if (change_scroll_region && save_cursor
  1968.      && restore_cursor && scroll_reverse)
  1969.     has_scroll_region = TRUE;
  1970.     if (insert_line && delete_line)
  1971.     has_insdel_line = TRUE;
  1972.     if (set_window && scroll_reverse)
  1973.     has_scroll_window = TRUE;
  1974.  
  1975.      /* Init window structure array.
  1976.       */
  1977.     topw = botw = lastw = -1;
  1978.     for (w=0; w<MAXWINDOWS; w++)
  1979.     win[w].flags = 0;
  1980.  
  1981.      /* Set up save/restore file name.
  1982.       * If there is a file '.wmrc' in the current directory,
  1983.       * use it.  Otherwise use .wmrc in the user's home directory.
  1984.       */
  1985.     if (*savefile == '\0')
  1986.     {
  1987.     if (access(".wmrc",0) == 0)
  1988.         strcpy(savefile, ".wmrc");
  1989.     else if (getenv("HOME") != NULL) {
  1990.         (void)sprintf(savefile, "%s/.wmrc", getenv("HOME"));
  1991.         if (access(savefile,0) != 0)
  1992.         *savefile = '\0';
  1993.     }
  1994.     }
  1995. }
  1996.  
  1997. /*
  1998.  * Shut down WM and exit.
  1999.  */
  2000. XShutdown(code)
  2001.  
  2002. int code;    /* exit code */
  2003. {
  2004.     register int w;    /* window pointer */
  2005.  
  2006.  
  2007.      /* Kill processes associated with each window.
  2008.       */
  2009.     for (w=0; w<MAXWINDOWS; w++)
  2010.     if (win[w].flags&INUSE) { KillShell(w); FreeWindow(w); }
  2011.  
  2012.     (void) movecursor(LINES-1, 0);
  2013.     endwin();
  2014.     putchar('\n');
  2015.  
  2016.     exit(code);
  2017. }
  2018.  
  2019. /*
  2020.  * Check each pty for input.
  2021.  * If present, read input and send it to that window.
  2022.  * The readmask from the last 'select()' call tells us
  2023.  * which pty's have input pending.
  2024.  */
  2025. readptys(rmask)
  2026.  
  2027. register int rmask;    /* IN: read mask from last 'select()' call */
  2028. {
  2029.     char buf[LINEBUF];    /* input buffer */
  2030.     register int w;    /* window */
  2031. #ifdef oldway
  2032.     register int i;    /* index */
  2033. #endif
  2034.     register int n;    /* number of bytes pending on read */
  2035.  
  2036.  
  2037.     for (w=botw; w>=0; w=win[w].next)
  2038.     {
  2039.     if ((rmask & (01<<win[w].pty)) == 0)
  2040.         continue;
  2041.  
  2042.      /* If window is blocked, notify user that window
  2043.       * has pending output.
  2044.       */
  2045.     if (win[w].flags&BLOCKED)
  2046.     {
  2047.         DisablePty(w);
  2048.         showmsg("\007Output pending in window #%d.", w);
  2049.         continue;
  2050.     }
  2051.  
  2052.      /* Read and process output for window w.
  2053.       */
  2054.     n = read(win[w].pty, buf, LINEBUF/Divisor);
  2055.     if (n <= 0)
  2056.         continue;
  2057.     WMaddbuf(w, buf, n);
  2058.     wrefresh(win[w].wptr);
  2059.     if (clamp_down)
  2060.         if ((clamp_down -= n) < 0)
  2061.         clamp_down = 0;
  2062.     }
  2063.  
  2064.     RestoreCursor();
  2065. }
  2066.  
  2067. /*
  2068.  * Signal handler.
  2069.  */
  2070. onintr(n)
  2071. {
  2072.     (void)signal(n, SIG_IGN);
  2073.     Shutdown(1);
  2074. }
  2075.  
  2076. /*
  2077.  * Signal handler for SIGCHLD
  2078.  * (received whenever a window shell dies).
  2079.  */
  2080. sigchild()
  2081. {
  2082.     (void) signal(SIGCHLD, sigchild);     /* not needed in 4.2bsd */
  2083.     childdied = TRUE;
  2084. }
  2085.  
  2086. /*
  2087.  * Clean up after dead window shell.
  2088.  */
  2089. ReapShell()
  2090. {
  2091.     register int w;    /* window index */
  2092.     register int pid;    /* process id of child */
  2093.     register int pgrp;    /* process group of child */
  2094.     union wait status;
  2095.  
  2096.  
  2097.      /* Reset flag.
  2098.       */
  2099.     childdied = FALSE;
  2100.  
  2101.      /* Figure out which children died,
  2102.       * clean up after them.
  2103.       */
  2104.     while ((pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0)) > 0)
  2105.     {
  2106.     /* It is truly amazing how complex simple things can become */
  2107.     if (WIFSTOPPED(status)) {
  2108.         if (status.w_stopsig == SIGTTOU || status.w_stopsig == SIGTTIN)
  2109.         continue;    /* Let's not worry about these */
  2110.         showmsg("Cannot suspend a window shell.");
  2111.         RestoreCursor();
  2112.         if ((pgrp = getpgrp(pid)) <= 0 || killpg(pgrp, SIGCONT))
  2113.         (void) kill(pid, SIGCONT);    /* so there! */
  2114.         continue;
  2115.     }
  2116.     for (w=botw; w>=0; w=win[w].next)
  2117.         if (win[w].pid == pid)
  2118.         {
  2119.         DisablePty(w);
  2120.         KillShell(w);
  2121.         win[w].pid = -1;
  2122.         break; /* out of for loop */
  2123.         }
  2124.     }
  2125. }
  2126.  
  2127. /*
  2128.  * Enable pty of window w for reading.
  2129.  */
  2130. EnablePty(w)
  2131.  
  2132. register int w;    /* window whose pty we're enabling */
  2133. {
  2134.     if (win[w].pid > 0)
  2135.     savereadmask |=  (01 << win[w].pty);
  2136. }
  2137.  
  2138. /*
  2139.  * Disable pty of window w for reading.
  2140.  */
  2141. DisablePty(w)
  2142.  
  2143. register int w;    /* window whose pty we're disabling */
  2144. {
  2145.     if (win[w].pid > 0)
  2146.     savereadmask &= ~(01 << win[w].pty);
  2147. }
  2148. SHAR_EOF
  2149. if test 12415 -ne "`wc -c < 'wm.c'`"
  2150. then
  2151.     echo shar: error transmitting "'wm.c'" '(should have been 12415 characters)'
  2152. fi
  2153. fi # end of overwriting check
  2154. #    End of shell archive
  2155. exit 0
  2156.